查看原文
其他

自写保护壳交流——指令的膨胀和花指令的插入

Wszzy 看雪学院 2019-05-25

从准备写壳到到现在已经有半年多的时间了,期间代码框架重构了很多次。


总体架构共有三部分:PE部分、混淆虚拟化部分、保护部分。


目前 PE部分只写了简单的操作,没有对代码段的压缩和对导入表的处理。虚拟化部分,框架大概写蹦了5、6次(目前可以做到简单指令的模拟和对重定位的支持)最近一次是在2个月以前,将架构改成vmp 3.x的链式寻址的时候,又写蹦了框架,现在还没有整合。


本帖就说一下近期在写的混淆部分:


目前在写代码混淆膨胀部分,目前的设定框架是由3块组成:

1. 代码块的拆分

2. 全局地址管理器

3. 混淆过程



一、代码块的拆分




二、混淆地址管理器


混淆地址管理器,也叫全局地址管理器,因为vm框架也是用这个地址管理器。 在代码混淆过程中,你并不知道接下来的代码区块将会被膨胀到多大,所以必须要一个地址管理器来确定下一区块的大小。


首先要将地址分块,按照去除重定位(401000 处加载基址)的方式分配地址:

代码块1:address 0x40B016 

代码块2:address  xxx

代码块3:address  xxx


使用地址管理器将代码块的地方分配并连接起来达到如下效果:




混淆分块后效果图:





三、混淆过程步骤


使用 代码块的拆分+地址管理器 -> 将代码块划分成一个个小的地址快来进行管理


首先划分区块,并将区块打上属性标签。


1. 区块划分

[DBGLOG]:代码块0,区块属性 JCC

mov eax, 0x1

cmp eax, 0x2

jz 0x40100e


[DBGLOG]:代码块1,区块属性 NULL

add eax, 0x1

inc eax


[DBGLOG]:代码块2,区块属性 JCC

mov eax, eax

add eax, 0x1

cmp eax, 0x1

jnz 0x40101d


[DBGLOG]:代码块3,区块属性 NULL

mov eax, eax

add eax, 0x1


[DBGLOG]:代码块4,区块属性 JCC

add eax, 0x1

dec eax

cmp eax, 0x2

jz 0x40102b


[DBGLOG]:代码块5,区块属性 NULL

mov eax, eax

add eax, 0x1


[DBGLOG]:代码块6,区块属性 JCC

mov eax, eax

mov eax, eax

dec eax

cmp eax, 0x3

jnz 0x40103a


[DBGLOG]:代码块7,区块属性 NULL

mov eax, eax

add eax, 0x1


[DBGLOG]:代码块8,区块属性 End

sub eax, 0x1

mov eax, eax


2. 将代码区块使用地址管理器分配地址:

[DBGLOG] 地址:00401000,代码块:0

[DBGLOG] 地址:0040100a,代码块:1

[DBGLOG] 地址:0040100e,代码块:2

[DBGLOG] 地址:00401018,代码块:3

[DBGLOG] 地址:0040101d,代码块:4

[DBGLOG] 地址:00401026,代码块:5

[DBGLOG] 地址:0040102b,代码块:6

[DBGLOG] 地址:00401035,代码块:7

[DBGLOG] 地址:0040103a,代码块:8


3. 添加简易重定位支持

这里对于混淆引擎来说使用到的全局变量应该交由PE部分去处理,但我这边偷懒直接使用vmp的重定位方式(/尴尬)


4. 指令处理

对于混淆虚拟化来说代码的本质是等价替换,这里以举一个最简单的例子 push 0x123456 :


我的方式 主要是对0x123456 进行处理,首先拿到0x123456 这个立即数 对0x123456进行加法,


异或 等操作 imm = ( 0x123456+0x111111 ) ^ 0x22222 = 0x216745在混淆代码中表现出来:


push eax
mov eax,imm
xor eax,0x22222
sub eax,0x111111
push eax
mov eax,dword ptr[esp+0x4]


这样就完成了 对 push 0x123456 指令的混淆。


5. 花指令的插入

在全局 有一个控制花指令插入的开关 _flower_code 由它决定是否插入花指令,部分代码如下:


void CodeObfuscationEngine::_insert_flower_code(asmjit::X86Assembler *& _a)
{
if (_insert_junk_code_number % 100 > _flower_code)
return;

int r = rand() % 3;
switch (r)
{
case 0:
{
Label label = _a->newLabel();
_a->jmp(label);
_a->db(0xE8);
_a->bind(label);
}break;
case 1:
{
Label label = _a->newLabel();
_a->jmp(label);
_a->db(0xE9);
_a->bind(label);
}break;
case 2:
{
Label label = _a->newLabel();
int addr = rand() % 0x66 + 0x5;
_a->call(label);
_a->db(0x74);
_a->db(addr);
_a->bind(label);
_a->add(esp, 4);
}break;
default:
break;
}


正常代码块:




代码块拆分:




代码混淆部分+花部分 :


里面去除了对某些指令的膨胀(比方xor)相对比较简单,新手项可以看未加壳部分,要求是逆出key


CrackMe :

Crackme_pack.exe(点击左下角阅读原文下载)


原始的未混淆部分:

Crackme_原始.exe(点击左下角阅读原文下载)



写在最后:

自己的工程参考了很多很多代码(多到我也记不清了,就不一一署名),先谢谢前辈们无私的奉献。





- End -




看雪ID:Wszzy           

https://bbs.pediy.com/user-770239.htm



本文由看雪论坛 Wszzy 原创

转载请注明来自看雪社区



热门图书推荐

 立即购买!





热门文章阅读

1、API监控+代码乱序的壳

2、Tcache利用总结

3、看雪课程 |  LLVM 编译框架详解

4、SandHook 第四弹 | Android Q 支持 & Inline 的特别处理




公众号ID:ikanxue

官方微博:看雪安全

商务合作:wsc@kanxue.com




↙点击下方“阅读原文”,查看更多干货!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存